﻿using System.Collections.Concurrent;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Vixen.Framework;

namespace Vixen.Collections
{
    /// <summary>Encapsulates a collection of Sprites that make up a screen together.</summary>
    public class Screen
    {
        public ConcurrentDictionary<uint, Sprite> Sprites; // The collection of Sprites

        /// <summary>Creates a new Screen variable and leaves the collection of Sprites empty.</summary>
        public Screen()
        {
            Sprites = new ConcurrentDictionary<uint, Sprite>();
        }

        /// <summary>Creates a new Screen variable and passes the parameter as the Screen's Sprite collection.</summary>
        /// <param name="spriteArg">The collection of Sprites to make up the Screen.</param>
        public Screen(IEnumerable<Sprite> spriteArg)
        {
            Sprites = new ConcurrentDictionary<uint, Sprite>();
            foreach (var s in spriteArg)
            {
                Sprites.TryAdd(0, s);
            }
        }

        /// <summary>Adds a single Sprite to the current collection of Sprites belonging to the Screen.</summary>
        /// <param name="sprite"></param>
        public void AddSprite(Sprite sprite)
        {
            Sprites.TryAdd(0, sprite);
        }

        /// <summary>Adds a collection of Sprites to the current collection of Sprites belonging to the Screen.</summary>
        /// <param name="sprites"></param>
        public void AddSprites(IEnumerable<Sprite> sprites)
        {
            foreach (var s in sprites)
                Sprites.TryAdd(0, s);
        }

        /// <summary>Removes a Sprite from the collection using a UID number.</summary>
        /// <param name="uid">The unique identification number used to identify Sprites.</param>
        public void RemoveSprite(uint uid)
        {
            Sprite sprite;
            Sprites.TryRemove(uid, out sprite);
        }

        /// <summary>Removes a Sprite from the collection using a Sprite value.</summary>
        /// <param name="spriteValue">The Sprite to be removed from the collection.</param>
        public void RemoveSprite(Sprite spriteValue)
        {
            Sprite sprite;
            Sprites.TryRemove(spriteValue.UID, out sprite);
        }

        /// <summary>Removes multiple Sprites from the collection using a collection of UID numbers.</summary>
        /// <param name="uidArg">The collection of Sprite UID values to remove from the collection.</param>
        public void RemoveSprites(IEnumerable<uint> uidArg)
        {
            Sprite sprite;
            foreach (var uid in uidArg)
                Sprites.TryRemove(uid, out sprite);
        }

        /// <summary>Removes multiple Sprites from the collection using a collection of Sprites.</summary>
        /// <param name="spriteArg">The collection of Sprites to remove from the collection.</param>
        public void RemoveSprites(IEnumerable<Sprite> spriteArg)
        {
            Sprite sprite;
            foreach (var s in spriteArg)
                Sprites.TryRemove(s.UID, out sprite);
        }

        /// <summary>Calls the update method on every Sprite within this Screen.</summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public void Update(GameTime gameTime)
        {
            foreach (var s in Sprites.Values)
                s.Update(gameTime);
        }

        /// <summary>Calls the update method on every Sprite within this Screen.</summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        /// <param name="prevKs">The previous state of the keyboard (last update call).</param>
        /// <param name="newKs">The current state of the keyboard (this update call).</param>
        public void Update(GameTime gameTime, KeyboardState prevKs, KeyboardState newKs)
        {
            foreach (var s in Sprites.Values)
                s.Update(gameTime, prevKs, newKs);
        }

        /// <summary>Calls the update method on every Sprite within this Screen.</summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        /// <param name="prevMs">The previous state of the mouse (last update call).</param>
        /// <param name="newMs">The current state of the mouse (this update call).</param>
        public void Update(GameTime gameTime, MouseState prevMs, MouseState newMs)
        {
            foreach (var s in Sprites.Values)
                s.Update(gameTime, prevMs, newMs);
        }

        /// <summary>Calls the update method on every Sprite within this Screen.</summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        /// <param name="prevKs">The previous state of the keyboard (last update call).</param>
        /// <param name="newKs">The current state of the keyboard (this update call).</param>
        /// <param name="prevMs">The previous state of the mouse (last update call).</param>
        /// <param name="newMs">The current state of the mouse (this update call).</param>
        public void Update(GameTime gameTime, KeyboardState prevKs, KeyboardState newKs, MouseState prevMs,
                           MouseState newMs)
        {
            foreach (var s in Sprites.Values)
                s.Update(gameTime, prevKs, newKs, prevMs, newMs);
        }

        /// <summary>Calls the drawing method on every Sprite within this Screen.</summary>
        /// <param name="spriteBatch">The SpriteBatch used to draw the Sprite to the screen.</param>
        public void Draw(SpriteBatch spriteBatch)
        {
            foreach (var s in Sprites.Values)
                s.Draw(spriteBatch, true);
        }
    }
}